
#include <iostream>

#include "QmitkImageFill.h"
#include <QAction>

#include <QInputDialog>
#include <QMessageBox>
#include <QCheckBox>
#include <QSpinBox>
#include <QSlider>
#include <vtkRenderWindow.h>
#include <mitkContour.h>



#include <mitkRenderingManager.h>
#include <mitkProperties.h>
#include <mitkGlobalInteraction.h>
#include "mitkUndoController.h"
#include "mitkIDataStorageService.h"
#include "mitkNodePredicateDataType.h"
#include "mitkNodePredicateAnd.h"
#include "mitkNodePredicateProperty.h"

#include <itkCommand.h>

#include "WxContourMapperOverlay.h"
#include "WxRenderToContourCutter.h"

//to be moved to mitkInteractionConst.h by StateMachineEditor
const mitk::OperationType QmitkImageFill::OP_EXCHANGE = 717;

// constructors for operation classes
QmitkImageFill::opExchangeNodes::opExchangeNodes( mitk::OperationType type, mitk::DataNode* node, mitk::BaseData* oldData, mitk::BaseData* newData )
:mitk::Operation(type),m_Node(node),m_OldData(oldData),m_NewData(newData), m_NodeDeletedObserverTag(0), m_OldDataDeletedObserverTag(0),
m_NewDataDeletedObserverTag(0)
{
  // listen to the node the image is hold
  itk::MemberCommand<opExchangeNodes>::Pointer nodeDeletedCommand = itk::MemberCommand<opExchangeNodes>::New();
  nodeDeletedCommand->SetCallbackFunction(this, &opExchangeNodes::NodeDeleted);

  m_NodeDeletedObserverTag = m_Node->AddObserver(itk::DeleteEvent(), nodeDeletedCommand);
  m_OldDataDeletedObserverTag = m_OldData->AddObserver(itk::DeleteEvent(), nodeDeletedCommand);
  m_NewDataDeletedObserverTag = m_NewData->AddObserver(itk::DeleteEvent(), nodeDeletedCommand);
}

// destructor for operation class
QmitkImageFill::opExchangeNodes::~opExchangeNodes()
{
  if (m_Node != NULL)
  {
    m_Node->RemoveObserver(m_NodeDeletedObserverTag);
    m_Node=NULL;
  }

  if (m_OldData.IsNotNull())
  {
    m_OldData->RemoveObserver(m_OldDataDeletedObserverTag);
    m_OldData=NULL;
  }

  if (m_NewData.IsNotNull())
  {
    m_NewData->RemoveObserver(m_NewDataDeletedObserverTag);
    m_NewData=NULL;
  }
}

void QmitkImageFill::opExchangeNodes::NodeDeleted(const itk::Object * /*caller*/, const itk::EventObject &/*event*/)
{
  m_Node = NULL;
  m_OldData = NULL;
  m_NewData = NULL;
}

QmitkImageFill::QmitkImageFill(QObject *parent)
: QObject(parent),
m_Controls(NULL)
{
}


QmitkImageFill::~QmitkImageFill()
{
  //delete smart pointer objects

}

void QmitkImageFill::CreateQtPartControl(QWidget* parent)
{
  if (!m_Controls)
  {
    // build ui elements
    m_Controls = new Ui::QmitkImageFillControls;
    m_Controls->setupUi(parent);

    // setup ui elements
    m_Controls->groupInfo->hide();
    m_Controls->m_SurroundingSlider->show();
    m_Controls->m_SurroundingSpin->show();
    m_Controls->m_TLGrayvalue->show();
    m_Controls->btnMakeContour->setEnabled(true);

    // create ui element connections
    this->CreateConnections();

    m_Controls->cmbImage->SetDataStorage(this->GetDefaultDataStorage());

    mitk::NodePredicateDataType::Pointer p1 = mitk::NodePredicateDataType::New("Image");
    mitk::NodePredicateProperty::Pointer p2 = mitk::NodePredicateProperty::New("visible", mitk::BoolProperty::New(true));
    mitk::NodePredicateAnd::Pointer predicate = mitk::NodePredicateAnd::New();
    predicate->AddPredicate(p1);
    predicate->AddPredicate(p2);

    m_Controls->cmbImage->SetPredicate(predicate);
}

}

void QmitkImageFill::CreateConnections()
{
  if ( m_Controls )
  {
    connect( m_Controls->btnMakeContour, SIGNAL(clicked()), this, SLOT(MakeContour()));
    connect( m_Controls->btnFillIn, SIGNAL(clicked()), this, SLOT(FillIn()) );
    connect( m_Controls->btnFillOut, SIGNAL(clicked()), this, SLOT(FillOut()) );
    connect( m_Controls->btnClearContour, SIGNAL(clicked()), this, SLOT(ClearContour()) );
    connect( m_Controls->chkInformation, SIGNAL(toggled(bool)), this, SLOT(ChkInformationToggled(bool)) );
    connect( m_Controls->cmbImage, SIGNAL(OnSelectionChanged(const mitk::DataNode*)), this, SLOT(OnImageSelectionChanged(const mitk::DataNode*)) );
  }
}

void QmitkImageFill::Activated()
{
  QmitkFunctionality::Activated();  // just call the inherited function
}


void QmitkImageFill::Deactivated()
{

  QmitkFunctionality::Deactivated(); // just call the inherited function

  mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}

/*! When called with an opExchangeNodes, it changes the content of a node from one data set to another
*/
void QmitkImageFill::ExecuteOperation (mitk::Operation *operation)
{
  if (!operation) return;

  switch (operation->GetOperationType())
  {
  case OP_EXCHANGE:
    {
      //RemoveBoundingObjectFromNode();
      opExchangeNodes* op = static_cast<opExchangeNodes*>(operation);
      op->GetNode()->SetData(op->GetNewData());
      mitk::RenderingManager::GetInstance()->InitializeViews();
      mitk::RenderingManager::GetInstance()->RequestUpdateAll();
      break;
    }
  default:;
  }

}

void QmitkImageFill::OnSelectionChanged( std::vector<mitk::DataNode*> nodes )
{
  mitk::DataNode* selectedNode = 0;
  if(nodes.size() > 0)
    selectedNode = nodes.front();

    disconnect( m_Controls->cmbImage, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ),
             this, SLOT( OnComboBoxSelectionChanged( const mitk::DataNode* ) ) );

    m_Controls->cmbImage->setCurrentIndex( m_Controls->cmbImage->Find(selectedNode) );

    connect( m_Controls->cmbImage, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ),
      this, SLOT( OnComboBoxSelectionChanged( const mitk::DataNode* ) ) );
}

const mitk::DataNode::Pointer QmitkImageFill::selectedImage()
{
  return m_Controls->cmbImage->GetSelectedNode();
}


void QmitkImageFill::ChkInformationToggled( bool on )
{
  if (on)
    m_Controls->groupInfo->show();
  else
    m_Controls->groupInfo->hide();
}

void QmitkImageFill::StdMultiWidgetAvailable( QmitkStdMultiWidget&  stdMultiWidget )
{
  m_MultiWidget = &stdMultiWidget;
}

void QmitkImageFill::StdMultiWidgetNotAvailable()
{
  m_MultiWidget = NULL;
}

void QmitkImageFill::NodeRemoved(const mitk::DataNode *node)
{

}

void QmitkImageFill::MakeContour()
{
    mitk::Contour::Pointer contour=mitk::Contour::New();
    mitk::DataNode::Pointer _CurrentcontourNode = mitk::DataNode::New();
    _CurrentcontourNode->SetData(contour);
    _CurrentcontourNode->SetMapper(mitk::BaseRenderer::Standard3D,mitk::WxContourMapperOverlay::New());
    _CurrentcontourNode->SetMapper(mitk::BaseRenderer::Standard2D,mitk::WxContourMapperOverlay::New());

    this->GetDataStorage()->Add(_CurrentcontourNode);
    m_ContourInteractor=mitk::WxContourToolOverlay::New("PressMoveRelease",_CurrentcontourNode);

    //this->GetActiveStdMultiWidget()->DisableNavigationControllerEventListening();
    mitk::GlobalInteraction::GetInstance()->AddListener(m_ContourInteractor);
    m_Controls->btnFillIn->setEnabled(true);
    m_Controls->btnFillOut->setEnabled(true);
    m_Controls->btnClearContour->setEnabled(true);
    m_Controls->btnMakeContour->setEnabled(false);
}

void QmitkImageFill::FillIn()
{
    m_ImageNode = this->selectedImage();
    if (m_ImageNode.IsNull()) return;
      m_ImageToCrop = dynamic_cast<mitk::Image*>(m_ImageNode->GetData());
    // test, if image is selected
    if (m_ImageToCrop.IsNull()) return;

    mitk::WxRenderToContourCutter::Pointer cutter=mitk::WxRenderToContourCutter::New();
    cutter->SetInsideValue(m_Controls->m_SurroundingSpin->value());
    mitk::DataNode* dataNode=this->m_ContourInteractor->GetContourNode();
    if(dataNode==NULL) return;
    mitk::Contour* contour = dynamic_cast<mitk::Contour*>(dataNode->GetData());
    cutter->SetOverlayContour(contour);
    cutter->SetInput(m_ImageToCrop);
    cutter->SetRenderer(m_ContourInteractor->GetContourOnVtkRenderer());
    cutter->Update();
    mitk::Image::Pointer resultImage = cutter->GetOutput();
    resultImage->DisconnectPipeline();

    {
        opExchangeNodes*  doOp   = new opExchangeNodes(OP_EXCHANGE, m_ImageNode.GetPointer(),
                                                       m_ImageNode->GetData(),
                                                       resultImage);
        opExchangeNodes* undoOp  = new opExchangeNodes(OP_EXCHANGE, m_ImageNode.GetPointer(),
                                                       resultImage,
                                                       m_ImageNode->GetData());

        mitk::UndoController::GetCurrentUndoModel()->SetOperationEvent(
                new mitk::OperationEvent(this, doOp, undoOp, "Crop image") ); // tell the undo controller about the action
        ExecuteOperation(doOp); // execute action
    }
    this->GetDataStorage()->Remove(dataNode);
    mitk::GlobalInteraction::GetInstance()->RemoveListener(m_ContourInteractor);
    mitk::RenderingManager::GetInstance()->ForceImmediateUpdateAll();
    m_ContourInteractor=NULL;

    m_Controls->btnClearContour->setEnabled(false);
    m_Controls->btnFillIn->setEnabled(false);
    m_Controls->btnFillOut->setEnabled(false);
    m_Controls->btnMakeContour->setEnabled(true);

}


void QmitkImageFill::FillOut()
{
    m_ImageNode = this->selectedImage();
    if (m_ImageNode.IsNull()) return;
      m_ImageToCrop = dynamic_cast<mitk::Image*>(m_ImageNode->GetData());
    // test, if image is selected
    if (m_ImageToCrop.IsNull()) return;

    mitk::WxRenderToContourCutter::Pointer cutter=mitk::WxRenderToContourCutter::New();
    cutter->SetInsideValue(m_Controls->m_SurroundingSpin->value());
    cutter->SetFillInside(false);
    mitk::DataNode* dataNode=this->m_ContourInteractor->GetContourNode();
    if(dataNode==NULL) return;
    mitk::Contour* contour = dynamic_cast<mitk::Contour*>(dataNode->GetData());
    cutter->SetOverlayContour(contour);
    cutter->SetInput(m_ImageToCrop);
    cutter->SetRenderer(m_ContourInteractor->GetContourOnVtkRenderer());
    cutter->Update();
    mitk::Image::Pointer resultImage = cutter->GetOutput();
    resultImage->DisconnectPipeline();

    {
        opExchangeNodes*  doOp   = new opExchangeNodes(OP_EXCHANGE, m_ImageNode.GetPointer(),
                                                       m_ImageNode->GetData(),
                                                       resultImage);
        opExchangeNodes* undoOp  = new opExchangeNodes(OP_EXCHANGE, m_ImageNode.GetPointer(),
                                                       resultImage,
                                                       m_ImageNode->GetData());

        mitk::UndoController::GetCurrentUndoModel()->SetOperationEvent(
                new mitk::OperationEvent(this, doOp, undoOp, "Crop image") ); // tell the undo controller about the action
        ExecuteOperation(doOp); // execute action
    }
    this->GetDataStorage()->Remove(dataNode);
    mitk::GlobalInteraction::GetInstance()->RemoveListener(m_ContourInteractor);
    mitk::RenderingManager::GetInstance()->ForceImmediateUpdateAll();

    m_Controls->btnClearContour->setEnabled(false);
    m_Controls->btnFillIn->setEnabled(false);
    m_Controls->btnFillOut->setEnabled(false);
    m_Controls->btnMakeContour->setEnabled(true);
}
void QmitkImageFill::ClearContour()
{
    if(m_ContourInteractor.IsNull()) return;
    mitk::DataNode* dataNode=this->m_ContourInteractor->GetContourNode();
    if(dataNode==NULL) return;
    this->GetDataStorage()->Remove(dataNode);
    mitk::GlobalInteraction::GetInstance()->RemoveListener(m_ContourInteractor);
    mitk::RenderingManager::GetInstance()->ForceImmediateUpdateAll();

    m_Controls->btnClearContour->setEnabled(false);
    m_Controls->btnFillIn->setEnabled(false);
    m_Controls->btnFillOut->setEnabled(false);
    m_Controls->btnMakeContour->setEnabled(true);
}
